#include <iostream>
#include <sstream>
#include <unicode/utypes.h>
#include <unicode/ucnv.h>
#include "UnicodeConverter.h"

using namespace kc_sf;

UnicodeConverter::UnicodeConverter()
: u_codepage( "ascii" )
{
	CreateConverter();
}

UnicodeConverter::UnicodeConverter( const std::string &codepage )
: u_codepage( codepage )
{
	CreateConverter();
}

UnicodeConverter::~UnicodeConverter()
{
	ucnv_close( u_converter );
}

// --------------------------------------------------------------- //

bool UnicodeConverter::encode( const std::string &input, UnicodeString &output ) const
{
	output = UnicodeString();
    if( input.empty()  )
    	return true;

	const char *input_cstr = input.c_str();
	int32_t input_size = input.size() + 1;
	UErrorCode status = U_ZERO_ERROR;

	output = UnicodeString(
		input_cstr,
		input_size,
		u_converter,
		status );

	if( !U_SUCCESS( status ) )
		std::cout << "!U_SUCCESS: " << status << std::endl;

	return U_SUCCESS( status );
}

// --------------------------------------------------------------- //

UnicodeString UnicodeConverter::encode( const std::string &input ) const
{
	UnicodeString output;
	if( !encode( input, output ) )
		throw UnicodeConverterError( "Błąd konwersji stron kodowych (" + u_codepage + " -> Unicode)" );

	return output;
}

// --------------------------------------------------------------- //

bool UnicodeConverter::decode( const UnicodeString &input, std::string &output ) const
{
	output.clear();
    if( input.isEmpty() )
    	return true;

	UErrorCode err = U_ZERO_ERROR;
	unsigned long size = input.length() * 2 + 1;
	char *dest = new char[size];

	input.extract( dest, size, u_converter, err );
	output = dest;

	return U_SUCCESS( err );
}

// --------------------------------------------------------------- //

std::string UnicodeConverter::decode( const UnicodeString &input  ) const
{
	std::string output;
	if( !decode( input, output ) )
		throw UnicodeConverterError( "Błąd konwersji stron kodowych (Unicode -> " + u_codepage + ")" );

	return output;
}

// --------------------------------------------------------------- //

void UnicodeConverter::CreateConverter()
{
	UErrorCode err = U_ZERO_ERROR;
	u_converter = ucnv_open( u_codepage.c_str(), &err );

	if( U_SUCCESS( err ) )
	{
		InitConverter();
	}
	else
		throw UnicodeConverterError( "Błąd przy tworzeniu konwertera stron kodowych (ICU). Errno: " + err );
}

void UnicodeConverter::InitConverter( )
{
	UErrorCode err = U_ZERO_ERROR;
	const void *old_context;
	UConverterFromUCallback old_from_action;
	UConverterToUCallback old_to_action;

	// stop when ILLEGAL/TRUNCATED/INVALID sequences encountered
	// Unicode -> Codepage
	ucnv_setFromUCallBack(
		u_converter,
		UCNV_FROM_U_CALLBACK_STOP,
		NULL,
		&old_from_action,
		&old_context,
		&err );

	if( U_FAILURE( err ) )
	{
		std::stringstream err_stream;
		err_stream << "Błąd tworzenia konwertera stron kodowych (Unicode -> "
			<< u_codepage << "). Errno: " << err;
		throw UnicodeConverterError( err_stream.str() );
	}

	// stop when ILLEGAL/TRUNCATED/INVALID sequences encountered
	// Codepage -> Unicode
	ucnv_setToUCallBack(
		u_converter,
		UCNV_TO_U_CALLBACK_SUBSTITUTE,
		UCNV_SUB_STOP_ON_ILLEGAL,
		&old_to_action,
		&old_context,
		&err );

	if( U_FAILURE( err ) )
	{
		std::stringstream err_stream;
		err_stream << "Błąd tworzenia konwertera stron kodowych ("
			<< u_codepage << " -> Unicode). Errno: " << err;
		throw UnicodeConverterError( err_stream.str() );
	}
}
